/* eslint-disable unicorn/no-fn-reference-in-iterator */
/* globals jQuery */
/**
 * Localizing script for SVG-edit UI
 * @module locale
 * @license MIT
 *
 * @copyright 2010 Narendra Sisodya
 * @copyright 2010 Alexis Deveria
 *
 */

/**
 * Used, for example, in the ImageLibs extension, to present libraries (with name/URL/description) in order
 * @typedef {GenericArray<module:locale.LocaleStrings>} module:locale.LocaleArray
 */
/**
 * The string keys of the object are two-letter language codes
 * @tutorial LocaleDocs
 * @typedef {PlainObject<string, string|module:locale.LocaleStrings|module:locale.LocaleArray>} module:locale.LocaleStrings
 */
// keyed to an array of objects with "id" and "title" or "textContent" properties
/**
 * @typedef {PlainObject<string, string>} module:locale.LocaleSelectorValue
 */

import { importSetGlobalDefault } from '../external/dynamic-import-polyfill/importModule.js'

const $ = jQuery

let langParam

/**
 * Looks for elements to localize using the supplied `obj` to indicate
 *   on which selectors (or IDs if `ids` is set to `true`) to set its
 *   strings (with selectors relative to the editor root element). All
 *   keys will be translated, but for each selector, only the first item
 *   found matching will be modified.
 * If the type is `content`, the selector-identified element's children
 *   will be checked, and the first (non-empty) text (placeholder) node
 *   found will have its text replaced.
 * If the type is `title`, the element's `title`
 *   property will be set.
 * If the type is `aria-label`, the element's `aria-label` attribute
 *   will be set (i.e., instructions for screen readers when there is
 *   otherwise no visible text to be read for the function of the form
 *   control).
 * @param {"content"|"title"} type
 * @param {module:locale.LocaleSelectorValue} obj Selectors or IDs keyed to strings
 * @param {boolean} ids
 * @returns {void}
 */
export const setStrings = function(type, obj, ids) {
    // Root element to look for element from
    const parent = $('#svg_editor').parent()
    Object.entries(obj).forEach(([sel, val]) => {
        if (!val) {
            console.log(sel) // eslint-disable-line no-console
        }

        if (ids) {
            sel = '#' + sel
        }
        const $elem = parent.find(sel)
        if ($elem.length) {
            const elem = $elem[0]

            switch (type) {
                case 'aria-label':
                    elem.setAttribute('aria-label', val)
                    break
                case 'content':
                    ;[...elem.childNodes].some(node => {
                        if (
                            node.nodeType === 3 /* Node.TEXT_NODE */ &&
                            node.textContent.trim()
                        ) {
                            node.textContent = val
                            return true
                        }
                        return false
                    })
                    break

                case 'title':
                    elem.title = val
                    break
            }
        } else {
            console.log('Missing element for localization: ' + sel) // eslint-disable-line no-console
        }
    })
}

/**
 * The "data" property is generally set to an an array of objects with
 *   "id" and "title" or "textContent" properties
 * @typedef {PlainObject} module:locale.AddLangExtensionLocaleData
 * @property {module:locale.LocaleStrings[]} data See {@tutorial LocaleDocs}
 */

/**
 * @interface module:locale.LocaleEditorInit
 */
/**
 * @function module:locale.LocaleEditorInit#addLangData
 * @param {string} langParam
 * @returns {module:locale.AddLangExtensionLocaleData}
 */

let editor_
/**
 * Sets the current editor instance (on which `addLangData`) exists.
 * @function init
 * @memberof module:locale
 * @param {module:locale.LocaleEditorInit} editor
 * @returns {void}
 */
export const init = editor => {
    editor_ = editor
}

/**
 * @typedef {PlainObject} module:locale.LangAndData
 * @property {string} langParam
 * @property {module:locale.LocaleStrings} langData
 */

/**
 * @function module:locale.readLang
 * @param {module:locale.LocaleStrings} langData See {@tutorial LocaleDocs}
 * @fires module:svgcanvas.SvgCanvas#event:ext_addLangData
 * @returns {Promise<module:locale.LangAndData>} Resolves to [`LangAndData`]{@link module:locale.LangAndData}
 */
export const readLang = async function(langData) {
    const more = await editor_.addLangData(langParam)
    $.each(more, function(i, m) {
        if (m.data) {
            langData = $.merge(langData, m.data)
        }
    })

    // Old locale file, do nothing for now.
    if (!langData.tools) {
        return undefined
    }

    const {
        tools,
        // misc,
        properties,
        config,
        layers,
        common,
        ui
    } = langData

    setStrings(
        'content',
        {
            // Todo: Add this powered by (probably by default) but with config to remove
            // copyrightLabel: misc.powered_by, // Currently commented out in svg-editor.html
            curve_segments: properties.curve_segments,
            fitToContent: tools.fitToContent,
            fit_to_all: tools.fit_to_all,
            fit_to_canvas: tools.fit_to_canvas,
            fit_to_layer_content: tools.fit_to_layer_content,
            fit_to_sel: tools.fit_to_sel,

            largest_object: tools.largest_object,

            layersLabel: layers.layers,
            page: tools.page,
            relativeToLabel: tools.relativeTo,
            selLayerLabel: layers.move_elems_to,
            selectedPredefined: config.select_predefined,

            selected_objects: tools.selected_objects,
            smallest_object: tools.smallest_object,
            straight_segments: properties.straight_segments,

            svginfo_bg_url: config.editor_img_url + ':',
            svginfo_bg_note: config.editor_bg_note,
            svginfo_dim: config.doc_dims,
            svginfo_editor_prefs: config.editor_prefs,
            svginfo_height: common.height,
            svginfo_image_props: config.image_props,
            svginfo_lang: config.language,
            svginfo_title: config.doc_title,
            svginfo_width: common.width,

            tool_docprops_cancel: common.cancel,
            tool_docprops_save: common.ok,

            tool_source_cancel: common.cancel,
            tool_source_save: common.ok,

            tool_prefs_cancel: common.cancel,
            tool_prefs_save: common.ok,

            sidepanel_handle: layers.layers.split('').join(' '),

            tool_clear: tools.new_doc,
            tool_docprops: tools.docprops,
            tool_export: tools.export_img,
            tool_import: tools.import_doc,
            tool_open: tools.open_doc,
            tool_save: tools.save_doc,

            svginfo_units_rulers: config.units_and_rulers,
            svginfo_rulers_onoff: config.show_rulers,
            svginfo_unit: config.base_unit,

            svginfo_grid_settings: config.grid,
            svginfo_snap_onoff: config.snapping_onoff,
            svginfo_snap_step: config.snapping_stepsize,
            svginfo_grid_color: config.grid_color
        },
        true
    )

    // Context menus
    const opts = {}
    ;[
        'cut',
        'copy',
        'paste',
        'paste_in_place',
        'delete',
        'group',
        'ungroup',
        'move_front',
        'move_up',
        'move_down',
        'move_back'
    ].forEach(item => {
        opts['#cmenu_canvas a[href="#' + item + '"]'] = tools[item]
    })

    ;['dupe', 'merge_down', 'merge_all'].forEach(item => {
        opts['#cmenu_layers a[href="#' + item + '"]'] = layers[item]
    })

    opts['#cmenu_layers a[href="#delete"]'] = layers.del

    setStrings('content', opts)

    const ariaLabels = {}
    Object.entries({
        tool_blur: properties.blur,
        tool_position: tools.align_to_page,
        tool_font_family: properties.font_family,
        zoom_panel: ui.zoom_level,
        stroke_linejoin: properties.linejoin_miter,
        stroke_linecap: properties.linecap_butt,
        tool_opacity: properties.opacity
    }).forEach(([id, value]) => {
        ariaLabels['#' + id + ' button'] = value
    })
    Object.entries({
        group_opacity: properties.opacity,
        zoom: ui.zoom_level
    }).forEach(([id, value]) => {
        ariaLabels['#' + id] = value
    })
    setStrings('aria-label', ariaLabels)

    setStrings(
        'title',
        {
            align_relative_to: tools.align_relative_to,
            circle_cx: properties.circle_cx,
            circle_cy: properties.circle_cy,
            circle_r: properties.circle_r,
            cornerRadiusLabel: properties.corner_radius,
            ellipse_cx: properties.ellipse_cx,
            ellipse_cy: properties.ellipse_cy,
            ellipse_rx: properties.ellipse_rx,
            ellipse_ry: properties.ellipse_ry,
            fill_color: properties.fill_color,
            font_family: properties.font_family,
            idLabel: properties.id,
            image_height: properties.image_height,
            image_url: properties.image_url,
            image_width: properties.image_width,
            layer_delete: layers.del,
            layer_down: layers.move_down,
            layer_new: layers.new,
            layer_rename: layers.rename,
            layer_moreopts: common.more_opts,
            layer_up: layers.move_up,
            line_x1: properties.line_x1,
            line_x2: properties.line_x2,
            line_y1: properties.line_y1,
            line_y2: properties.line_y2,
            linecap_butt: properties.linecap_butt,
            linecap_round: properties.linecap_round,
            linecap_square: properties.linecap_square,
            linejoin_bevel: properties.linejoin_bevel,
            linejoin_miter: properties.linejoin_miter,
            linejoin_round: properties.linejoin_round,
            main_icon: tools.main_menu,
            palette: ui.palette_info,
            zoom_panel: ui.zoom_level,
            path_node_x: properties.node_x,
            path_node_y: properties.node_y,
            rect_height_tool: properties.rect_height,
            rect_width_tool: properties.rect_width,
            seg_type: properties.seg_type,
            selLayerNames: layers.move_selected,
            selected_x: properties.pos_x,
            selected_y: properties.pos_y,
            stroke_color: properties.stroke_color,
            stroke_style: properties.stroke_style,
            stroke_width: properties.stroke_width,
            svginfo_title: config.doc_title,
            text: properties.text_contents,
            toggle_stroke_tools: ui.toggle_stroke_tools,
            tool_add_subpath: tools.add_subpath,
            tool_alignbottom: tools.align_bottom,
            tool_aligncenter: tools.align_center,
            tool_alignleft: tools.align_left,
            tool_alignmiddle: tools.align_middle,
            tool_alignright: tools.align_right,
            tool_aligntop: tools.align_top,
            tool_angle: properties.angle,
            tool_blur: properties.blur,
            tool_bold: properties.bold,
            tool_circle: tools.mode_circle,
            tool_clone: tools.clone,
            tool_clone_multi: tools.clone,
            tool_delete: tools.del,
            tool_delete_multi: tools.del,
            tool_ellipse: tools.mode_ellipse,
            tool_fhellipse: tools.mode_fhellipse,
            tool_fhpath: tools.mode_fhpath,
            tool_fhrect: tools.mode_fhrect,
            tool_font_size: properties.font_size,
            tool_group_elements: tools.group_elements,
            tool_make_link: tools.make_link,
            tool_link_url: tools.set_link_url,
            tool_image: tools.mode_image,
            tool_italic: properties.italic,
            tool_line: tools.mode_line,
            tool_move_bottom: tools.move_bottom,
            tool_move_top: tools.move_top,
            tool_node_clone: tools.node_clone,
            tool_node_delete: tools.node_delete,
            tool_node_link: tools.node_link,
            tool_opacity: properties.opacity,
            tool_openclose_path: tools.openclose_path,
            tool_path: tools.mode_path,
            tool_position: tools.align_to_page,
            tool_rect: tools.mode_rect,
            tool_redo: tools.redo,
            tool_reorient: tools.reorient_path,
            tool_select: tools.mode_select,
            tool_source: tools.source_save,
            tool_square: tools.mode_square,
            tool_text: tools.mode_text,
            tool_topath: tools.to_path,
            tool_undo: tools.undo,
            tool_ungroup: tools.ungroup,
            tool_wireframe: tools.wireframe_mode,
            tool_zoom: tools.mode_zoom,
            url_notice: tools.no_embed
        },
        true
    )

    return { langParam, langData }
}

/**
 *
 * @function module:locale.putLocale
 * @param {string} givenParam
 * @param {string[]} goodLangs
 * @param {{langPath: string}} conf
 * @fires module:svgcanvas.SvgCanvas#event:ext_addLangData
 * @fires module:svgcanvas.SvgCanvas#event:ext_langReady
 * @fires module:svgcanvas.SvgCanvas#event:ext_langChanged
 * @returns {Promise<module:locale.LangAndData>} Resolves to result of {@link module:locale.readLang}
 */
export const putLocale = async function(givenParam, goodLangs, conf) {
    if (givenParam) {
        langParam = givenParam
    } else {
        langParam = $.pref('lang')
        if (!langParam) {
            if (navigator.userLanguage) {
                // Explorer
                langParam = navigator.userLanguage
            } else if (navigator.language) {
                // FF, Opera, ...
                langParam = navigator.language
            }
        }

        console.log('Lang: ' + langParam) // eslint-disable-line no-console

        // Set to English if language is not in list of good langs
        if (!goodLangs.includes(langParam) && langParam !== 'test') {
            langParam = 'zh-CN'
        }

        // don't bother on first run if language is English
        // The following line prevents setLang from running
        //    extensions which depend on updated uiStrings,
        //    so commenting it out.
        // if (langParam.startsWith('en')) {return;}
    }

    const url = conf.langPath + 'lang.' + langParam + '.js'
    return readLang(
        // Todo: Replace this with `return import(url);` when
        //   `import()` widely supported
        await importSetGlobalDefault(url, {
            global: 'svgEditorLang_' + langParam.replace(/-/g, '_')
        })
    )
}
